package middleware

import (
	"fmt"
	"net"
	"os"
	"runtime"
	"runtime/debug"
	"strings"

	"github.com/getsentry/raven-go"
	"github.com/gin-gonic/gin"

	"mychat/internal/common/cmctx"
	"mychat/internal/common/errors"
	"mychat/internal/common/logger"
)

func Recovery(lg logger.Logger, defaultErr *errors.Error) gin.HandlerFunc {
	lg = lg.WithCustomTag(logger.LoggerTagRecovery)
	return func(gtx *gin.Context) {
		raven.CapturePanic(
			func() {
				defer func() {
					if e := recover(); e != nil {
						var brokenPipe bool
						if ne, ok := e.(*net.OpError); ok {
							if se, ok := ne.Err.(*os.SyscallError); ok {
								if strings.Contains(strings.ToLower(se.Error()),
									"broken pipe") || strings.Contains(strings.ToLower(se.Error()),
									"connection reset by peer") {
									brokenPipe = true
								}
							}
						}

						var reqBody []byte
						var url string
						func() {
							defer func() {
								if e := recover(); e != nil {
									lg.ErrorCtx(gtx, "recovery get body panic||err=%+v||stack=%s",
										e, string(debug.Stack()))
								}
							}()

							url = gtx.Request.URL.String()
							body, ok := gtx.Get(gin.BodyBytesKey)
							if ok {
								reqBody, ok = body.([]byte)
								if ok {
									if len(reqBody) > 1536 {
										reqBody = reqBody[:1536]
									}
								}
							}
						}()

						lg.WarningCtx(gtx, "panic||err=%v||stack=%v||url=%s||body=%v",
							e, string(debug.Stack()), url, string(reqBody))

						if !brokenPipe {
							cmctx.ReplyJsonAPIError(gtx, defaultErr)
							switch e.(type) {
							case runtime.Error:
								panic(fmt.Sprintf("%s;\nurl=%s;\nrequestId=%s;\nbody=%s",
									e.(runtime.Error).Error(), url, cmctx.GetGtxRequestId(gtx), string(reqBody)))
							default:
								panic(e)
							}
						} else {
							gtx.Abort()
						}
					}
				}()
				gtx.Next()
			}, nil)
	}
}
